//
//  GLViewController.h
//  Pirate Flag
//
//  Created by jeff on 4/8/09.
//  Copyright Jeff LaMarche 2009. All rights reserved.
//

#import "GLViewController.h"
#import "GLView.h"
#import "OpenGLCommon.h"
#import "ConstantsAndMacros.h"

@implementation GLViewController
@synthesize flagTexture;
- (void)drawView:(GLView*)view;
{
    // Set light blue background
    glClearColor(0.68, 0.84, 0.90, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glLoadIdentity(); 
    
    glRotatef(-90.0, 0.0, 0.0, 1.0);
    glTranslatef(-17.5, -11.0, -35.0);
    
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_TEXTURE);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);    
    glEnableClientState(GL_NORMAL_ARRAY);    
    
    if (flagTexture != nil)
        [flagTexture bind];
    else
        [OpenGLTexture3D useDefaultTexture];
    
    int vertexCounter = 0;
    int texCoordCounter = 0;
    int normalCounter = 0;
    for (int x = 0; x < FLAG_X_POINTS-1; x++)
    {
        for (int y = 0; y < FLAG_Y_POINTS-1; y++)
        {
            vertices[vertexCounter++] = flagVertices[x][y];
            vertices[vertexCounter++] = flagVertices[x+1][y];
            normals[normalCounter++] = flagVertexNormals[x][y];
            normals[normalCounter++] = flagVertexNormals[x+1][y];
            
            // Calculate the texture coordinates for the two triangles
            texCoords[texCoordCounter++] = (GLfloat)x * 1.0 / (GLfloat)(FLAG_X_POINTS);
            texCoords[texCoordCounter++] = 1 - ((GLfloat)y * 1.0 / (GLfloat)(FLAG_Y_POINTS));
            texCoords[texCoordCounter++] = (GLfloat)(x+1) * 1.0 / (GLfloat)(FLAG_X_POINTS);
            texCoords[texCoordCounter++] = 1 - ((GLfloat)(y) * 1.0 / (GLfloat)(FLAG_Y_POINTS));
        }
        
        glVertexPointer(3, GL_FLOAT, 0, vertices); 
        glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
        glNormalPointer(GL_FLOAT, 0, normals);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, stripVertexCount);
        vertexCounter = 0;
        texCoordCounter = 0;
        normalCounter = 0;
    }
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_TEXTURE);
    glDisableClientState(GL_NORMAL_ARRAY);
    glDisable(GL_TEXTURE_2D);
    
    // Move the flag vertices along sine wave
    for (int y =0 ; y< FLAG_Y_POINTS - 1; y++)
    {
        GLfloat vertexWrap = flagVertices[FLAG_X_POINTS -1][y].z;
        Vertex3D normalWrap = flagVertexNormals[FLAG_X_POINTS-1][y];
        for (int x = FLAG_X_POINTS -1; x >= 0 - 1; x--)
        {
            flagVertices[x][y].z = flagVertices[x-1][y].z;
            flagVertexNormals[x][y] = flagVertexNormals[x-1][y];
        }
        flagVertices[0][y].z = vertexWrap;
        flagVertexNormals[0][y] = normalWrap;
    }
}

-(void)setupView:(GLView*)view
{
	const GLfloat			lightAmbient[] = {0.25, 0.25, 0.27, 1.0};
	const GLfloat			lightDiffuse[] = {0.35, 0.35, 0.35, 1.0};
    
	const GLfloat			lightPosition[] = {35.0,35.0, 100, 0.0}; 
	const GLfloat			light2Position[] = {-35.0, -35.0, 35, 0.0};
	const GLfloat			lightShininess = 0.0;
	const GLfloat			zNear = 0.01, zFar = 1000.0, fieldOfView = 45.0; 
	GLfloat size; 
	glEnable(GL_DEPTH_TEST);
	glMatrixMode(GL_PROJECTION);
	
	size = zNear * tanf(DEGREES_TO_RADIANS(fieldOfView) / 2.0); 
	CGRect rect = view.bounds; 
	glFrustumf(-size, size, -size / (rect.size.width / rect.size.height), size / 
			   (rect.size.width / rect.size.height), zNear, zFar); 
	glViewport(0, 0, rect.size.width, rect.size.height);  
	glMatrixMode(GL_MODELVIEW);
	glShadeModel(GL_SMOOTH);
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CCW);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); 
	glLightfv(GL_LIGHT0, GL_SHININESS, &lightShininess);
	
	glEnable(GL_LIGHT1);
	glLightfv(GL_LIGHT1, GL_AMBIENT, lightAmbient);
	glLightfv(GL_LIGHT1, GL_DIFFUSE, lightDiffuse);
	glLightfv(GL_LIGHT1, GL_POSITION, light2Position); 
	glLightfv(GL_LIGHT1, GL_SHININESS, &lightShininess);
	
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
	glEnable(GL_BLEND);
	
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
	glLoadIdentity(); 
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 	
	
	
	glGetError(); // Clear error codes
    
    // Setup flag points
    for (int x = 0; x < FLAG_X_POINTS; x++)
    {
        for (int y = 0; y < FLAG_Y_POINTS; y++)
        {
            flagVertices[x][y].x = (GLfloat)x;
            flagVertices[x][y].y = (GLfloat)y;
            
            GLfloat yPoints = (GLfloat)FLAG_Y_POINTS+5;
            GLfloat sinVal = ((GLfloat)x*yPoints / 360.0) * 2.0 * M_PI;
            flagVertices[x][y].z = (GLfloat)sin(sinVal);
        }
    }
    
    // Calculate Flag Vertex Normals - this might need a little explanation
    
    // All the internal vertices (those not on a border of the flag) are part of
    // seven polygons. To calculate their vertices, we have to calculate the vertices
    // of all seven triangles and average them.
    
    for (int x = 1; x < FLAG_X_POINTS-1; x++)
    {
        for (int y = 1; y < FLAG_Y_POINTS-1; y++)
        {
            Vertex3D vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][y], flagVertices[x-1][y], flagVertices[x][y-1]));
            Vertex3D vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][y], flagVertices[x][y-1], flagVertices[x+1][y-1]));
            Vertex3D vertex3 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][y], flagVertices[x+1][y-1], flagVertices[x+1][y]));
            Vertex3D vertex4 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][y], flagVertices[x+1][y], flagVertices[x+1][y+1]));            
            Vertex3D vertex5 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][y], flagVertices[x][y+1], flagVertices[x-1][y+1]));
            Vertex3D vertex6 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][y], flagVertices[x-1][y+1], flagVertices[x-1][y]));
            
            flagVertexNormals[x][y].x = (vertex1.x + vertex2.x + vertex3.x + vertex4.x + vertex5.x + vertex6.x) / 6.0; 
            flagVertexNormals[x][y].y = (vertex1.y + vertex2.y + vertex3.y + vertex4.y + vertex5.y + vertex6.y) / 6.0;
            flagVertexNormals[x][y].z = (vertex1.z + vertex2.z + vertex3.z + vertex4.z + vertex5.z + vertex6.z) / 6.0;
            Vector3DNormalize(&flagVertexNormals[x][y]);
        }
    }
    
    // Top and Bottom Strip, excluding top left and bottom right are part of three polygons
    for (int x = 0; x < FLAG_X_POINTS; x++)
    {
        // Calculate for top strip
        if (x > 0)
        {
            Vertex3D vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][0], flagVertices[x-1][1], flagVertices[x-1][0]));
            Vertex3D vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][0], flagVertices[x][1], flagVertices[x-1][1]));
            Vertex3D vertex3 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][0], flagVertices[x+1][0], flagVertices[x][1]));
            flagVertexNormals[x][0].x = (vertex1.x + vertex2.x + vertex3.x) / 3.0;
            flagVertexNormals[x][0].y = (vertex1.y + vertex2.y + vertex3.y) / 3.0;
            flagVertexNormals[x][0].z = (vertex1.z + vertex2.z + vertex3.z) / 3.0;
            Vector3DNormalize(&flagVertexNormals[x][0]);
        }
        
        // Calculate for bottom strip
        if (x < FLAG_X_POINTS)
        {
            Vertex3D vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][FLAG_Y_POINTS-1], flagVertices[x-1][FLAG_Y_POINTS-1], flagVertices[x][FLAG_Y_POINTS-2]));
            Vertex3D vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][FLAG_Y_POINTS-1], flagVertices[x][FLAG_Y_POINTS-2], flagVertices[x+1][FLAG_Y_POINTS-2]));
            Vertex3D vertex3 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[x][FLAG_Y_POINTS-1], flagVertices[x+1][FLAG_Y_POINTS-2], flagVertices[x+1][FLAG_Y_POINTS-1]));
            flagVertexNormals[x][FLAG_Y_POINTS-1].x = (vertex1.x + vertex2.x + vertex3.x) / 3.0;
            flagVertexNormals[x][FLAG_Y_POINTS-1].y = (vertex1.y + vertex2.y + vertex3.y) / 3.0;
            flagVertexNormals[x][FLAG_Y_POINTS-1].z = (vertex1.z + vertex2.z + vertex3.z) / 3.0;
            Vector3DNormalize(&flagVertexNormals[x][FLAG_Y_POINTS-1]);
        }
    }
    
    // Same for left and right strips, except for corners
    for (int y = 0; y < FLAG_Y_POINTS; y++)
    {
        if (y > 0)
        {
            Vertex3D vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[0][y], flagVertices[0][y-1], flagVertices[1][y-1]));
            Vertex3D vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[0][y], flagVertices[1][y-1], flagVertices[1][y]));
            Vertex3D vertex3 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[0][y], flagVertices[1][y], flagVertices[0][y+1]));
            flagVertexNormals[0][y].x = (vertex1.x + vertex2.x + vertex3.x) / 3.0;
            flagVertexNormals[0][y].y = (vertex1.y + vertex2.y + vertex3.y) / 3.0;
            flagVertexNormals[0][y].z = (vertex1.z + vertex2.z + vertex3.z) / 3.0;
            Vector3DNormalize(&flagVertexNormals[0][y]);
        }
        if (y < FLAG_Y_POINTS)
        {
            Vertex3D vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[FLAG_X_POINTS-1][y], flagVertices[FLAG_X_POINTS-2][y], flagVertices[FLAG_X_POINTS-1][y-1]));
            Vertex3D vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[FLAG_X_POINTS-1][y], flagVertices[FLAG_X_POINTS-2][y+1], flagVertices[FLAG_X_POINTS-2][y]));
            Vertex3D vertex3 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[FLAG_X_POINTS-1][y], flagVertices[FLAG_X_POINTS-1][y+1], flagVertices[FLAG_X_POINTS-2][y+1]));
            flagVertexNormals[FLAG_X_POINTS-1][y].x = (vertex1.x + vertex2.x + vertex3.x) / 3.0;
            flagVertexNormals[FLAG_X_POINTS-1][y].y = (vertex1.y + vertex2.y + vertex3.y) / 3.0;
            flagVertexNormals[FLAG_X_POINTS-1][y].z = (vertex1.z + vertex2.z + vertex3.z) / 3.0;
            Vector3DNormalize(&flagVertexNormals[FLAG_X_POINTS-1][y]);
        }
    }
    
    // Upper right and lower left corners are part of two triangles
    Vertex3D vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[0][FLAG_Y_POINTS-1], flagVertices[0][FLAG_Y_POINTS-2], flagVertices[1][FLAG_Y_POINTS-2]));
    Vertex3D vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[0][FLAG_Y_POINTS-1], flagVertices[1][FLAG_Y_POINTS-2], flagVertices[1][FLAG_Y_POINTS-1]));
    flagVertexNormals[0][FLAG_Y_POINTS-1].x = (vertex1.x + vertex2.x) / 2.0;
    flagVertexNormals[0][FLAG_Y_POINTS-1].y = (vertex1.y + vertex2.y) / 2.0;
    flagVertexNormals[0][FLAG_Y_POINTS-1].z = (vertex1.z + vertex2.z) / 2.0;
    Vector3DNormalize(&flagVertexNormals[0][FLAG_Y_POINTS-1]);
    
    vertex1 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[FLAG_X_POINTS-1][0], flagVertices[FLAG_X_POINTS-2][1], flagVertices[FLAG_X_POINTS-2][0]));
    vertex2 = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[FLAG_X_POINTS-1][0], flagVertices[FLAG_X_POINTS-1][1], flagVertices[FLAG_X_POINTS-2][1]));
    flagVertexNormals[FLAG_X_POINTS-1][0].x = (vertex1.x + vertex2.x) / 2.0;
    flagVertexNormals[FLAG_X_POINTS-1][0].y = (vertex1.y + vertex2.y) / 2.0;
    flagVertexNormals[FLAG_X_POINTS-1][0].z = (vertex1.z + vertex2.z) / 2.0;
    Vector3DNormalize(&flagVertexNormals[FLAG_X_POINTS-1][0]);
    
    // Finally, top left and bottom right corners are part of one, so no averaging or normalzing needed
    flagVertexNormals[0][0] = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[0][0], flagVertices[1][0], flagVertices[0][1]));
    flagVertexNormals[FLAG_X_POINTS-1][FLAG_Y_POINTS-1] = Triangle3DCalculateSurfaceNormal(Triangle3DMake(flagVertices[FLAG_X_POINTS-1][FLAG_Y_POINTS-1], flagVertices[FLAG_X_POINTS-2][FLAG_Y_POINTS-1], flagVertices[FLAG_X_POINTS-1][FLAG_Y_POINTS-2]));
    
    
    // Setup texture
    OpenGLTexture3D *theTexture = [[OpenGLTexture3D alloc] initWithFilename:@"not_a_pirate.pvr4" width:512.0 height:512.0];
    self.flagTexture = theTexture;
    [theTexture release];
    
    // Allocate space for vertices & texture coordinates
    stripVertexCount = ((FLAG_Y_POINTS - 1) * 2);
    vertices = calloc(stripVertexCount, sizeof(Vertex3D));
    texCoords = calloc(stripVertexCount, sizeof(GLfloat) * 4);
    normals = calloc(stripVertexCount, sizeof(Vector3D));
}

- (void)didReceiveMemoryWarning 
{
    [super didReceiveMemoryWarning]; 
}

- (void)dealloc 
{
    [flagTexture release];
    if (vertices != NULL)
        free(vertices);
    if (texCoords != NULL)
        free(texCoords);
    if (normals != NULL)
        free(normals);
    [super dealloc];
}

@end
